
use axum::{extract::DefaultBodyLimit, Router};
use serde::Deserialize;
use tower_http::services::{ServeDir, ServeFile};

use super::cors;
type SevrDir = ServeDir<tower_http::set_status::SetStatus<ServeFile>>;

fn default_root() -> String {
    "html".to_owned()
}

fn default_web_addr() -> String {
    "0.0.0.0:3000".to_owned()
}

fn default_not_found() -> String {
    "404.html".to_owned()
}

static DEFAULT_BODY_LIMIT: usize = 5 * 1024 * 1024;

#[derive(Clone, Deserialize, Debug)]
#[allow(dead_code)]
pub struct WebConfig {
    /// 默认 0.0.0.0:3000
    #[serde(default = "default_web_addr")]
    pub addr: String,
    #[serde(default)]
    body_limit: Option<usize>,
    #[serde(default = "default_root")]
    root: String,
    /// localpath(:server_path)
    #[serde(default)]
    statics: Vec<String>,
    #[serde(default)]
    proxy: Vec<String>,
    #[serde(default = "default_not_found")]
    not_found_page: String,
    #[serde(default)]
    cors: Option<cors::CorsConfig>,
}

impl WebConfig {
    /// html的静态文件服务
    /// ```rust
    /// # fn test(cnf:web_config::Config, router:axum::Router::<()>){
    /// router.nest_service("/", cnf.web.html_server());
    /// # }
    /// ```
    pub fn html_server(&self) -> SevrDir {
        let srv = ServeDir::new(&self.root);
        srv.append_index_html_on_directories(true)
            .not_found_service(ServeFile::new(self._not_found_page()))
    }

    fn _not_found_page(&self) -> String {
        format!(
            "{}/{}",
            self.root.trim_end_matches('/'),
            self.not_found_page.trim_matches('/')
        )
    }

    /// 分割:,前半是文件目录,后半是url路径
    fn static_server_from_str(&self, stc: &str) -> (String, SevrDir) {
        let s: Vec<&str> = stc.split(":").collect();
        if s.is_empty() || s.len() > 2 {
            panic!("error web config of statics:\"{}\"", stc);
        };
        let root = unsafe { s.get_unchecked(0) };
        let prefix: &&str = s.get(1).unwrap_or(root);
        (
            prefix.to_string(),
            ServeDir::new(root)
                .append_index_html_on_directories(false)
                .not_found_service(ServeFile::new(self._not_found_page())),
        )
    }

    /// static目录的路由
    ///
    /// 对应配置文件的statics数组
    ///
    /// 分割:,前半是文件目录,后半是url路径
    /// ```rust
    /// # fn test(cnf:web_config::Config, router:axum::Router::<()>){
    /// router.merge(cnf.web.get_static_route());
    /// # }
    /// // or
    /// # fn test2(cnf:web_config::Config, router:axum::Router::<()>){
    /// router.nest("/", cnf.web.get_static_route());
    /// # }
    /// ```
    pub fn get_static_route<S>(&self) -> Router<S>
    where
        S: Send + Sync + Clone + 'static,
    {
        let mut router = Router::new();
        for stc in &self.statics {
            let (path, src) = self.static_server_from_str(stc);
            router = router.nest_service(&format!("/{}", path.trim_start_matches('/')), src);
        }
        router
    }

    /// 请求体限制的layer
    /// 默认大小：`5 * 1024 * 1024`
    pub fn body_limit_layer(&self) -> DefaultBodyLimit {
        DefaultBodyLimit::max(self.body_limit.unwrap_or(DEFAULT_BODY_LIMIT))
    }

    /// 跨域资源共享
    pub fn cors(&self) -> tower_http::cors::CorsLayer {
        self.cors
            .as_ref()
            .map(|cors| cors.create_cors_layer())
            .unwrap_or_default()
    }
}

impl Default for WebConfig {
    fn default() -> Self {
        Self {
            addr: default_web_addr(),
            not_found_page: "404.html".to_owned(),
            root: "html".to_owned(),
            cors: Default::default(),
            statics: vec![],
            proxy: vec![],
            body_limit: None,
        }
    }
}
